/*
 * voice.c
 *
 *		This file implements the wavetable oscillator, filter and
 *		amplitude envelope to create a 'voice' a complete unit of
 *		sound generation.
 *
 *      Author: Colin
 */

#include <voice.h>

// globals plus some initial values
static int32_t *sineWave;				// pointer to sine wave
static int32_t *sawWave;				// pointer to sawtooth wave
static int32_t *squareWave;				// pointer to square wave
static int32_t *oscWave;				// pointer for current oscillator waveform

static float currentFrequency = 0;		// frequency voice should produce
static float oscVol = 0.0;				// oscillator volume
static int32_t voiceState = voiceOff;	// voice state

static float filterAttackTime  = 0.5;	// filter envelope attack time
static float filterDecayTime   = 0.5;	// filter envelope decay time
static float filterReleaseTime = 0.4;	// filter envelope release time
static float filterMinFreq	   = 300;	// min filter frequency
static float filterMaxFreq	   = 10000;	// max filter frequency

static float envAttackTime 	= 0.01;		// volume envelope attack time
static float envDecayTime 	= 0.1;		// volume envelope decay time
static float envReleaseTime = 0.5;		// volume envelope release time

static float filterCutoff = 300;		// filter cut-off frequency
static float filterRes 	  = 0.8;		// filter resonance

/****************************************************************************
* DESCRIPTION: Initialise the voice, mainly wavetables
* RETURN:      none
*****************************************************************************/
void voice_init() {
	// init array for sine wave
	sineWave = wavetable_init(SINE);

	// init array for oscillator waveform
	sawWave = wavetable_init(SAW);

	// init array for sawtooth wave
	squareWave = wavetable_init(SQUARE);

	// current waveform is sawtooth
	oscWave = sawWave;
}

/****************************************************************************
* DESCRIPTION: Generate the next output sample
* RETURN:      int32_t containing sample amplitude value
*****************************************************************************/
int32_t voice_update() {
	// new sample
	int32_t sample;

	// filter update counter
	static int32_t filterUpdateCount = 0;

	// oscillator variables
	static fx_t 	fx_phaseIndex		= 0;
	static fx_t 	fx_phaseIncrease	= 0;
	static fx_t 	fx_fraction			= 0;
	static int32_t 	int_phaseIndex		= 0;

	// if voice is active
	if(voiceState != voiceOff) {
		// calculate fraction of table index increase
		fx_phaseIncrease = fx_make( currentFrequency / tableFreq );

		// oscillator
		sample = oscVol * linear_interpolation(fx_fraction, oscWave[int_phaseIndex], oscWave[int_phaseIndex+1]);

		// update cut-off frequency every sample
		filterCutoff = resFilter_freqEnvUpdate();

		/* update filter cut-off every 625 samples = 64 times a second
		 * to reduce overhead of calculating cut-off coefficients */
		if(filterUpdateCount >= filterUpdateRate) {
			resFilter_cutoff(filterCutoff, filterRes);
			filterUpdateCount = 0;
		}
		filterUpdateCount++;

		// send sample through filter
		sample = resFilter(sample);

		// send sample through amplitude envelope
		sample = envelope_update(sample);

		// increase phase index
		fx_phaseIndex += fx_phaseIncrease;

		// get integer phase index for wavetable index
		int_phaseIndex = fx_trunc_to_int32(fx_phaseIndex);

		// get fraction for linear interpolation
		fx_fraction = fx_get_fraction(fx_phaseIndex);

		/* reset phase once end of wavetable,
		 * tableSize-1 due to interpolation */
		if(int_phaseIndex >= tableSize-1) {
			fx_phaseIndex = 0;
			int_phaseIndex = 0;
		}
	}
	// if voice not active return empty sample
	else
		sample = 0;

	return sample;
}

/****************************************************************************
* DESCRIPTION: Get the current state of the voice.
* RETURN:      current state of voice as int32_t: -noteOn
* 			   									  -noteOff
* 												  -voiceOff
*****************************************************************************/
int32_t voice_state(void) {
	return voiceState;
}

/****************************************************************************
* DESCRIPTION: Signal note on to voice
* RETURN:      none
*****************************************************************************/
void voice_note_on(float frequency, float velocity) {
	currentFrequency = frequency;	// set frequency of voice
	oscVol = velocity;				// set velocity of note
	voiceState = noteOn;			// update voice state
	filterCutoff = filterMinFreq;	// set filter cutoff frequency

	// initialise filter
	resFilter_cutoff(filterMinFreq, filterRes);

	// initialise  envelopes
	envelope_init(envAttackTime, envDecayTime, envReleaseTime);
	resFilter_freqEnvInit(filterAttackTime, filterDecayTime, filterReleaseTime, filterMinFreq, filterMaxFreq);
}

/****************************************************************************
* DESCRIPTION: Signal note continuation, same as voice_note_on() except
* 			   envelope is not reset. Used for continuous note changes.
* RETURN:      none
*****************************************************************************/
void voice_note_on_continue(float frequency, float velocity) {
	currentFrequency = frequency;	// set frequency for voice
	oscVol = velocity;				// set velocity of note
	voiceState = noteOn;			// update voice state
}

/****************************************************************************
* DESCRIPTION: Signal note off message to voice. Set voice state and trigger
* 			   the envelopes to enter the release section
* RETURN:      none
*****************************************************************************/
void voice_note_off() {
	envelope_release();
	resFilter_freqEnvRelease();
	voiceState = noteOff;
}

/****************************************************************************
* DESCRIPTION: Turn voice completely off
* RETURN:      none
*****************************************************************************/
void voice_off() {
	currentFrequency = 0;
	voiceState = voiceOff;
}

/****************************************************************************
* DESCRIPTION: Select oscillator waveform based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_select_waveform(uint8_t controllerValue) {

	if(controllerValue < 0x2a) {
		// change waveform
		oscWave = sineWave;
	}
	else if(controllerValue > 0x2b && controllerValue < 0x53) {
		// change waveform
		oscWave = sawWave;
	}
	else if(controllerValue > 0x54) {
		// change waveform
		oscWave = squareWave;
	}
}

/****************************************************************************
* DESCRIPTION: Select amplitude envelope attack time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_amp_env_attack(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	envAttackTime = (controllerValue+1) * 0.0390625;
}

/****************************************************************************
* DESCRIPTION: Select amplitude envelope decay time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_amp_env_decay(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	envDecayTime = (controllerValue+1) * 0.0390625;
}

/****************************************************************************
* DESCRIPTION: Select amplitude envelope release time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_amp_env_release(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	envReleaseTime = (controllerValue+1) * 0.0390625;
}

/****************************************************************************
* DESCRIPTION: Update filter resonance based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_filter_resonance(uint8_t controllerValue) {
	// scale controllerValue to 0 - 1.0
	filterRes = controllerValue * 0.007874015748031;
}

/****************************************************************************
* DESCRIPTION: Select filter envelope attack time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_filter_env_attack(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	filterAttackTime = (controllerValue+1) * 0.0390625;
}

/****************************************************************************
* DESCRIPTION: Select filter envelope decay time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_filter_env_decay(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	filterDecayTime = (controllerValue+1) * 0.0390625;
}

/****************************************************************************
* DESCRIPTION: Select filter envelope release time based on controllerValue
* RETURN:      none
*****************************************************************************/
void voice_filter_env_release(uint8_t controllerValue) {
	// scale controllerValue to 0 - 5 seconds
	filterReleaseTime = (controllerValue+1) * 0.0390625;
}
